/*
 * Copyright (C) 2006 Moritz Tuerk <brotkasten@users.sourceforge.net>
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms 
 * of the GNU General Public License as published by the Free Software Foundation; either 
 * version 2 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License along with 
 *  this program; if not, write to the Free Software Foundation, 
 *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
 */
package mo.network.server.data;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class Fragment implements Comparable<Fragment> {
	/**
	 * 980 byte maximum payload per fragment
	 */
	public final static int MAX_PAYLOAD = 5000;

	/**
	 * Internal for serialization and deserialisation
	 */
	public final static int OVERHEAD = 48;

	/**
	 * This is used to deserialize a fragment. It creates a fragment from a
	 * serialized fragment.
	 * 
	 * @param data
	 *            saves the fragment as byte array
	 * @return the fragment
	 * @throws IOException
	 */
	public static Fragment deserialize(final byte[] data) throws IOException {
		final ByteArrayInputStream bis = new ByteArrayInputStream(data);

		final Fragment result = new Fragment();
		byte buf[] = new byte[8];

		bis.read(buf);
		result.size = (int) Long.parseLong(new String(buf), 16);

		buf = new byte[result.size - 8];
		bis.read(buf);

		int start = 0;
		int current = 0;
		int count = 0;

		while (count < 4) {
			if (buf[current] == '|') {
				switch (count) {
				case 0:
					result.prio =
							(int) Long.parseLong(
									new String(buf, start, current), 16);
					break;
				case 1:
					result.number =
							(int) Long.parseLong(new String(buf, start, current
									- start), 16);
					break;
				case 2:
					result.count =
							(int) Long.parseLong(new String(buf, start, current
									- start), 16);
					break;
				case 3:
					result.packetId =
							Long.parseLong(new String(buf, start, current
									- start), 16);
					break;
				}

				start = current + 1;
				count++;
			}
			current++;
		}
		result.type = (int) Long.parseLong(new String(buf, start, 8), 16);
		current += 7;

		result.size -= 16 + start;
		result.payload = new byte[buf.length - (start + 8)];
		System.arraycopy(buf, start + 8, result.payload, 0,
				result.payload.length);

		return result;
	}

	/**
	 * number of all fragments of this fragmeted packet
	 */
	int count;

	/**
	 * this is used by {@link Packet} and saves the last time this fragment
	 * was
	 * sent... this field is <b>not</b> serialized.
	 */
	long lastSendTime;

	/**
	 * number of this fragment
	 */
	int number;

	/**
	 * defines the id for the fragments packet. The server uses the ID to
	 * identify the fragments and their packets
	 */
	long packetId;

	/**
	 * the payload of this fragment
	 */
	byte[] payload;

	/**
	 * saves the priority of the packet
	 */
	int prio;

	/**
	 * this is used by {@link Packet} and saves the number of times this
	 * fragment was sent... this field is <b>not</b> serialized.
	 */
	int sendCount;

	/**
	 * size of this fragment (including header)
	 */
	int size;

	/**
	 * user defined field to distiguish between the different kinds of data
	 * send
	 */
	int type;

	public int compareTo(final Fragment other) {
		return this.number < other.number ? -1 : this.number > other.number ? 1
				: 0;
	}

	public int getCount() {
		return this.count;
	}

	public int getNumber() {
		return this.number;
	}

	private String getNumber(final long l, final int size) {
		final String tmp = Long.toHexString(l);
		final StringBuffer result = new StringBuffer(10);

		for (int i = 0; i < size - tmp.length(); i++) {
			result.append('0');
		}

		result.append(tmp);

		return result.toString();
	}

	public long getPacketId() {
		return this.packetId;
	}

	public byte[] getPayload() {
		return this.payload;
	}

	public int getPrio() {
		return this.prio;
	}

	public int getSize() {
		return this.size;
	}

	public int getType() {
		return this.type;
	}

	@Override
	public int hashCode() {
		return this.number;
	}

	public byte[] serialize() throws IOException {
		final ByteArrayOutputStream bos =
				new ByteArrayOutputStream(this.payload.length + 40);

		final StringBuffer buf = new StringBuffer(Fragment.OVERHEAD);

		buf.append(this.getNumber(this.prio, 0));
		buf.append("|");
		buf.append(this.getNumber(this.number, 0));
		buf.append("|");
		buf.append(this.getNumber(this.count, 0));
		buf.append("|");
		buf.append(this.getNumber(this.packetId, 0));
		buf.append("|");
		buf.append(this.getNumber(this.type, 8));

		bos.write(this.getNumber(this.size + 8 + buf.length(), 8).getBytes());
		bos.write(buf.toString().getBytes());

		bos.write(this.payload);

		bos.flush();
		bos.close();

		final byte result[] = bos.toByteArray();

		bos.close();

		return result;
	}

	@Override
	public String toString() {
		final StringBuffer result =
				new StringBuffer(
						"\n----------------------[FRAGMENT]-----------------------\n");
		result.append("type: " + this.type + "\n");
		result.append("pkid: " + this.packetId + "\n");
		result.append("no  : " + this.number + "\n");
		result.append("pl  : " + new String(this.payload) + "\n");
		result
				.append("----------------------[FRAGMENT]-----------------------\n");
		return result.toString();

	}
}
